package com.liuyu.common.framework.util.security;

import cn.hutool.setting.Setting;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.google.common.base.Splitter;
import com.google.common.collect.Lists;
import com.liuyu.common.framework.common.constant.SelfPowerConstants;
import com.liuyu.common.framework.common.service.security.model.PasswordResult;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author LiuYu
 * @Project:common-framework
 * @ClassName:PasswordMeterUtils
 * @Description:检测密码强度
 * @date 2019/6/1711:02
 * @Copyright:liuyu
 * @version:V1.0 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 * <p>
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 */
/**
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 */

/**
 * 密碼最低要求8字元
 * 最少符合下列四項中三項規則:
 * 大寫英文字元
 * 小寫英文字元
 * 數字字元
 * 符號字元
 * <p>
 * 增加字元的變化能提高分數.
 * 最後的分數為加分項目和減分項目的總和.
 * 分數的範圍為0~100分.
 * 分數不需達到最低字元即可計算.
 * <p>
 * 本人博客地址：http://blog.csdn.net/u010156024
 * 有什么问题 请留言或评论，我会及时回复。
 * 参考链接：
 * http://password.mx500.com/
 */

/**
 *
 * @author liuyu
 * @date 2021/4/8 15:14
 * @return
 */
@Slf4j
public class PasswordMeterUtils {


    public static final int CIPHER_STRENGTH_LIMIT = 60;
    private static final String WEAK_PASSWORD_LIST = "WEAK_PASSWORD_LIST";


    /**
     *密码
     */
    private static String psw;
    /**
     *     密码长度
     */
    private static int length;
    /**
     * 大写字母长度
     */
    private static int upperAlp = 0;
    /**
     * 小写字母长度
     */
    private static int lowerAlp = 0;
    /**
     * 数字长度
     */
    private static int num = 0;
    /**
     * 特殊字符长度
     */
    private static int charLen = 0;

    @Getter
    private static Set<String> weak = new HashSet<>();

    @Getter
    private static boolean keyboard;

    private static final Pattern NUMBER = Pattern.compile("[0-9]");
    private static final Pattern UPPER_LETTER = Pattern.compile("[A-Z]");
    private static final Pattern LOWER_LETTER = Pattern.compile("[a-z]");


    @Setter
    private static RedisTemplate redisTemplate;

    /**
     * 弱密码数据字典
     */
    private static List<String> DICTIONARY;

    private static Cache cache = Caffeine.newBuilder()
            //初始巨细
            .initialCapacity(128)
            //最大数量
            .maximumSize(1024)
            //过期时刻
            .expireAfterWrite(300, TimeUnit.SECONDS)
            .build();

    static {
        if (DICTIONARY == null || DICTIONARY.isEmpty()) {
            Setting setting = new Setting("weak/weak-password.setting");
            //配置文件变更是自动加载
            String password = setting.getByGroup("password", "default");
            log.info("加载弱口令字典,默认字典:{}", password);
            DICTIONARY = Lists.newArrayListWithCapacity(8);
            DICTIONARY.addAll(Splitter.on(",").trimResults().splitToList(password));
            try {
                setting = new Setting("weak/weak-extend.setting");
                password = setting.getByGroup("password", "extend");
                log.info("加载弱口令字典,扩展字典:{}", password);
                if (StringUtils.isNotBlank(password)) {
                    List<String> l = Splitter.on(",").trimResults().splitToList(password);
                    DICTIONARY.addAll(l);
                }
            } catch (Exception e) {
                log.warn("没有扩展弱口令字典:{}", e.getMessage(), e);
            }
        }
    }

    public static void setDICTIONARY(List<String> DICTIONARY) {

        PasswordMeterUtils.DICTIONARY = DICTIONARY;
    }


    private PasswordMeterUtils(String psw) {
        PasswordMeterUtils.psw = psw.replaceAll("\\s", "");
        length = psw.length();
    }

    /**
     * 密码长度积分
     * @author liuyu
     * @date 2021/4/8 14:35
     * @return
     */
    public static int CheckPswLength() {
        return length * 4;
    }


    /**
     * 大写字母积分
     * @author liuyu
     * @date 2021/4/8 15:53
     * @return
     */
    public static int CheckPswUpper() {
      /*  String reg = "[A-Z]";
        Pattern pattern = Pattern.compile(reg);*/
        Matcher matcher = UPPER_LETTER.matcher(psw);
        int j = 0;
        while (matcher.find()) {
            j++;
        }
        upperAlp = j;
        if (j <= 0) {
            return 0;
        }
        return (length - j) * 2;
    }

    /**
     * 测试小写字母字元
     * @author liuyu
     * @date 2021/4/8 15:52
     * @return
     */
    public static int CheckPwsLower() {
       /* String reg = "[a-z]";
        Pattern pattern = Pattern.compile(reg);*/
        Matcher matcher = LOWER_LETTER.matcher(psw);
        int j = 0;
        while (matcher.find()) {
            j++;
        }
        lowerAlp = j;
        if (j <= 0) {
            return 0;
        }
        return (length - j) * 2;
    }

    /**
     *    测试数字字元
     * @author liuyu
     * @date 2021/4/8 15:52
     * @return
     */
    public static int checkNum() {
      /*  String reg = "[0-9]";
        Pattern pattern = Pattern.compile(reg);*/
        Matcher matcher = NUMBER.matcher(psw);
        int j = 0;
        while (matcher.find()) {
            j++;
        }
        num = j;
        if (num == length) {
            return 0;
        }
        return j * 4;
    }

    /**
     *    测试符号字元
     * @author liuyu
     * @date 2021/4/8 15:52
     * @return
     */
    public static int checkChar() {
        charLen = length - upperAlp
                - lowerAlp - num;
        return charLen * 6;
    }

    /**
     *     密碼中間穿插數字或符號字元
     * @author liuyu
     * @date 2021/4/8 15:52
     * @return
     */
    public static int checkNumOrCharInStr() {
        int j = num + charLen - 1;
        if (j < 0) {
            j = 0;
        }
        if (num + charLen == length) {
            j = length - 2;
        }
        return j * 2;
    }

    /**
     * 最低要求标准
     * 该方法需要在以上加分方法使用后才可以使用
     *
     * @return
     */
    public static int LowerQuest() {
        int j = 0;
        if (length < 8) {
            j -= 4;
        }
        if (upperAlp > 0) {
            j++;
        }
        if (lowerAlp > 0) {
            j++;
        }
        if (num > 0) {
            j++;
        }
        if (charLen > 0) {
            j++;
        }
        if (j >= 3) {
            j = 1;
        } else {
            j = 0;
        }
        return j;
    }

    /**
     * =================分割线===扣分项目=====================
     **/


    /**
     * 弱密码以及弱密码段检测
     * @author liuyu
     * @date 2021/4/8 14:35
     * @param
     * @return int
     */
    public static int checkWeakPwd() {
        int l = 0;
        int size = 0;
        Set<String> weakPwdSet = (Set<String>) cache.getIfPresent(WEAK_PASSWORD_LIST);
        if (weakPwdSet == null) {
            synchronized (PasswordMeterUtils.class) {
                weakPwdSet = (Set<String>) cache.getIfPresent(WEAK_PASSWORD_LIST);
                if (weakPwdSet == null) {
                    if (redisTemplate != null) {
                        weakPwdSet = redisTemplate.opsForSet().members(SelfPowerConstants.CacheKeys.POWER_3_CLOUD_WEAK_PASSWORD_SET);
                        if (weakPwdSet != null) {
                            cache.put(WEAK_PASSWORD_LIST, weakPwdSet);
                        }
                    }
                }
            }
        }
        if (weakPwdSet == null) {
            weakPwdSet = new HashSet<>();
        }
        weakPwdSet.addAll(DICTIONARY);
        for (String s : weakPwdSet) {
            //匹配弱密码
            if (psw.equals(s)) {
                l -= 1000;
                weak.add(s);
            } else if (s.length() > 2 && psw.contains(s)) {
                size++;
                weak.add(s);
            }
        }
        //键盘连续4次以上
        if (isKeyBoardContinuousChar()) {
            size++;
        }
        l -= size * 50;
        return l;
    }

    /**
     * 键盘连续字符统计4个
     * @return
     */
    public static boolean isKeyBoardContinuousChar() {
        boolean result = false;
        char[][] c1 = {
                {'!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+'},
                {'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '{', '}', '|'},
                {'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ':', '"'},
                {'z', 'x', 'c', 'v', 'b', 'n', 'm', '<', '>', '?'}
        };
        char[][] c2 = {
                {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '='},
                {'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '{', '}', '\\'},
                {'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\''},
                {'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/'}
        };
//        String[] strList = str.split("");
//        //获取坐标位置
//        String[] y ;
//        String[] x ;

        for (char[][] c : new char[][][]{c1, c2}) {
            //横向
            for (int i = 0; i < c.length; i++) {
                for (int j = 0; j < c[i].length - 3; j++) {
                    //创建连续字符
                    StringBuffer sb = new StringBuffer();
                    for (int k = j; k < j + 4; k++) {
                        sb.append(c[i][k]);
                    }
                    String keyStr = sb.toString();
                    if (psw.contains(keyStr)) {
                        weak.add(keyStr);
                        keyboard = true;
                        return true;
                    }
                }
            }

            //纵向
            for (int i = 0; i < c[3].length; i++) {
                //创建连续字符--每列只有4个
                StringBuffer sb = new StringBuffer();
                for (int j = 0; j < 4; j++) {
                    sb.append(c[j][i]);
                }
                String keyStr = sb.toString();
                if (psw.contains(keyStr)) {
                    weak.add(keyStr);
                    keyboard = true;
                    return true;
                }
            }

        }
        return result;
    }


    /**
     *    只包含英文字母
     * @author liuyu
     * @date 2021/4/8 15:25
     * @return
     */
    public static int OnlyHasAlp() {
        if (length == (upperAlp + lowerAlp)) {
            return -length;
        }
        return 0;
    }

    /**
     *    只包含数字
     * @author liuyu
     * @date 2021/4/8 15:25
     * @return
     */
    public static int OnlyHasNum() {
        if (length == num) {
            return -length;
        }
        return 0;
    }

    /**
     * 重复字元扣分
     * @author liuyu
     * @date 2021/4/8 15:24
     * @return
     */
    public static int repeatDex() {
        char[] c = psw.toLowerCase().toCharArray();
        HashMap<Character, Integer> hashMap =
                new HashMap<Character, Integer>();
        for (int i = 0; i < c.length; i++) {
            if (hashMap.containsKey(c[i])) {
                hashMap.put(c[i], hashMap.get(c[i]) + 1);
            } else {
                hashMap.put(c[i], 1);
            }
        }
        int sum = 0;
        Iterator<Map.Entry<Character, Integer>> iterator =
                hashMap.entrySet().iterator();
        while (iterator.hasNext()) {
            int j = iterator.next().getValue();
            if (j > 0) {
                sum = sum + j * (j - 1);
            }
        }
        return -sum;
    }

    /**
     *     连续英文大写字元
     * @author liuyu
     * @date 2021/4/8 15:25
     * @return
     */
    public static int seriseUpperAlp() {
        int j = 0;
        char[] c = psw.toCharArray();
        for (int i = 0; i < c.length - 1; i++) {
            if (UPPER_LETTER.matcher(c[i] + "").find()) {
                if (UPPER_LETTER.matcher(c[i + 1] + "").find()) {
                    j++;
                }
            }
        }
        return -2 * j;
    }

    /**
     *     连续英文小写字元
     * @author liuyu
     * @date 2021/4/8 15:02
     * @return
     */
    public static int seriseLowerAlp() {
//        String reg = "[a-z]";
        int j = 0;
        char[] c = psw.toCharArray();
        for (int i = 0; i < c.length - 1; i++) {
            if (LOWER_LETTER.matcher(c[i] + "").find()
                    && c[i] + 1 == c[i + 1]) {
                j++;
            }
        }
        return -2 * j;
    }

    /**
     *    连续数字字元
     * @author liuyu
     * @date 2021/4/8 14:40
     * @return
     */
    public static int seriesNum() {
      /*  String reg = "[0-9]";
        Pattern pattern = Pattern.compile(reg);*/
        char[] c = psw.toCharArray();
        int j = 0;
        for (int i = 0; i < c.length - 1; i++) {
            if (NUMBER.matcher(c[i] + "").matches()
                    && NUMBER.matcher(c[i + 1] + "").matches()) {
                j++;
            }
        }
        return -2 * j;
    }

    /**
     * 连续字母abc def之类超过3个扣分  不区分大小写字母
     * @author liuyu
     * @date 2021/4/8 14:23
     * @return
     */
    public static int seriesAlp2Three() {
        int j = 0;
        char[] c = psw.toLowerCase(Locale.CHINA).toCharArray();
        for (int i = 0; i < c.length - 2; i++) {
            if (LOWER_LETTER.matcher(c[i] + "").find()) {
                if ((c[i + 1] == c[i] + 1) && (c[i + 2] == c[i] + 2)) {
                    j++;
                }
            }
        }
        return -3 * j;
    }

    /**
     *    连续数字123 234之类超过3个扣分
     * @author liuyu
     * @date 2021/4/8 14:39
     * @return
     */

    public static int seriesNum2Three() {
        int j = 0;
        char[] c = psw.toLowerCase(Locale.CHINA).toCharArray();
        for (int i = 0; i < c.length - 2; i++) {
            if (NUMBER.matcher(c[i] + "").find()) {
                if ((c[i + 1] == c[i] + 1) && (c[i + 2] == c[i] + 2)) {
                    j++;
                }
            }
        }
        return -3 * j;
    }


    public static int addScoreInfo() {
        System.out.println("密码长度=" + CheckPswLength());
        System.out.println("大写英文字母=" + CheckPswUpper());
        System.out.println("小写英文字母=" + CheckPwsLower());
        System.out.println("数字=" + checkNum());
        System.out.println("特殊符号=" + checkChar());
        System.out.println("密码中间穿插数字或者符号=" + checkNumOrCharInStr());
        System.out.println("已经项目最低密码要求=" + LowerQuest());
        System.out.println("加分项计算规则之和*最低密码要求,如果不符合最低密码要求，则加分项清零" + LowerQuest());
        return 0;
    }

    public static int subScoreInfo() {
        System.out.println("只有英文字符=" + OnlyHasAlp());
        System.out.println("只有数字字符=" + OnlyHasNum());
        System.out.println("重复字符(Case Insensitive)=" + repeatDex());
        System.out.println("连续英文大写字符=" + seriseUpperAlp());
        System.out.println("连续英文小写字符=" + seriseLowerAlp());
        System.out.println("连续数字字符=" + seriesNum());
        System.out.println("连续字母超过三个(如abc,def)=" + seriesAlp2Three());
        System.out.println("连续数字超过三个(如123,234)=" + seriesNum2Three());
        System.out.println("含有弱密码数据字典中的字符" + checkWeakPwd());
        return 0;
    }


    public static PasswordResult CheckPwdMeter(String password) {
        new PasswordMeterUtils(password);
        PasswordResult result = new PasswordResult();
        result.setPswLength(CheckPswLength());
        result.setPswUpper(CheckPswUpper());
        result.setPswLower(CheckPwsLower());
        result.setCheckNum(checkNum());
        result.setCheckChar(checkChar());
        result.setNumOrCharInStr(checkNumOrCharInStr());
        result.setLowerQuest(LowerQuest());

        result.setOnlyHasAlp(OnlyHasAlp());
        result.setOnlyHasNum(OnlyHasNum());
        result.setRepeatDex(repeatDex());
        result.setSeriesUpperAlp(seriseUpperAlp());
        result.setSeriesLowerAlp(seriseLowerAlp());
        result.setSeriesNum(seriesNum());
        result.setSeriesAlp2Three(seriesAlp2Three());
        result.setSeriesNum2Three(seriesNum2Three());
        result.setCheckWeakPwd(checkWeakPwd());
        result.setWeakSet(getWeak());
        if (result.getScore() < PasswordMeterUtils.CIPHER_STRENGTH_LIMIT) {
            result.setSuccess(Boolean.FALSE);
        } else {
            result.setSuccess(Boolean.TRUE);
        }
        return result;
    }


    public static void main(String[] args) {
        System.out.println(PasswordMeterUtils.CheckPwdMeter("qweraaaaWb134*5P@6"));
        PasswordMeterUtils.addScoreInfo();
        PasswordMeterUtils.subScoreInfo();
        System.out.println(PasswordMeterUtils.CheckPwdMeter("abc123234456"));
        PasswordMeterUtils.addScoreInfo();
        PasswordMeterUtils.subScoreInfo();
    }
}
